<?php
/***********************************************************************
 * @package     IvanEngine
 * @subpackage  Framwork
 * @author      Ivan <ivanzx@msn.com>
 * @created     2010-05-30
 ***********************************************************************/
if(defined("IVANENGINE_FRAMEWORK_PLUGINMANAGER"))
    die("Redefinition macro in :".__FILE__."(".__LINE__.")");
define("IVANENGINE_FRAMEWORK_PLUGINMANAGER", true);

require_once (dirname(__FILE__).'/Common.php');
require_once (dirname(__FILE__).'/Plugin.php');
require_once (dirname(__FILE__).'/IPlugins.php');

class CPluginManager extends CSingleton
{
    const  HOMEPAGE_NAME        = 'HomePage';
    static protected $m_this    = null;
    protected   $m_plugins      = null,
                $m_pluginMap    = null,
                $m_enableList   = null,
                $m_pluginsPath  = null,
                $m_internalPath = null;
    public function __construct()
    {
        parent::__construct();
        $this->m_pluginMap  = new CMap();
        $this->m_pluginsPath= CFileHelper::CorrectPath(CConfig::Option(CConfig::OT_PluginPath));
        $this->m_enableList = CConfig::Option(CConfig::OT_ActivePlugins);
        
        $this->m_internalPath   = CConfig::Option(CConfig::OT_InPluginPath);
        $this->m_internalPath   = $this->m_internalPath.M_DS.$pluginName;
        $this->m_internalPath   = CFileHelper::CorrectPath($this->m_internalPath);
    }
    
    public function __destruct()
    {
        $this->m_plugins->Shutdown();
        unset($this->m_plugins);
        unset($this->m_pluginMap);
        parent::__destruct();
    }
    
    static public function Get()
    {
        if( self::$m_this == null )
        {
            parent::StartSingleton();
            self::$m_this = new CPluginManager();
            parent::EndSingleton();
        }
        return self::$m_this;
    }
    
    public function Initialize()
    {
        if( $this->m_pluginsPath == null ){
            throw new CExceptionInvalidArgument('<b>PluginManager initialize failed:<b><br>'.
                'You must set plugin path in config file.');
        }
        if( !CFileHelper::IsExsit($this->m_pluginsPath) ){
            throw new CExceptionInvalidArgument('<b>PluginManager initialize failed:<b><br>'.
                'can not find the plugins folder ('.$this->m_pluginsPath.')');
        }
        if( !CFileHelper::IsExsit($this->GetPluginPath(self::HOMEPAGE_NAME)) ){
            throw new CException('<b>PluginManager initialize failed:<b><br>'.
                'can not find the "'.self::HOMEPAGE_NAME.'" plugins folder');
        }
        
        // load plugins class and init the class
        $pluginsFile    = $this->m_pluginsPath.M_DS.'Plugins.php';
        $pluginsFile    = CFileHelper::CorrectDS($pluginsFile);
        if( !CFileHelper::IsExsit($pluginsFile) ){
            throw new CException('<b>PluginManager initialize failed:<b><br>'.
                'can not find the Plugins.php in plugins folder');
        }
        require_once($pluginsFile);
        $pluginsClass   = 'CPlugins';
        if( !class_exists($pluginsClass) ){
            throw new CException('<b>PluginManager initialize failed:<b><br>'.
                'can not find the CPlugins class in Plugins.php');
        }
        $this->m_plugins= new $pluginsClass;
        $result = $this->m_plugins->Initialize($this);
        if( $result === null ){
            return true;
        }
        return $result;
    }
    
    public function Install($pluginName)
    {
        $plugin = $this->LoadPlugin($pluginName);
        if( $plugin )
            return true;
            
        require($this->GetPluginFile($pluginName));
        $class  = self::GetPluginClassName($pluginName);
        $plugin = new $class;
        $plugin->SetName($pluginName);
        $plugin->Initialize();
        return $plugin->Install();
    }
    
    public function Uninstall($pluginName)
    {
        $plugin = $this->LoadPlugin($pluginName);
        if( $plugin )
            return $plugin->Uninstall();
            
        return true;
    }
    
    public function LoadPlugins()
    {
        CLog::Get()->Note('PluginManager start loading plugins.');
        $result = new CMap();
        foreach( $this->m_enableList as $pluginName )
        {
            $tmpName    = CStringHelper::ToUpper($pluginName);
            $tmpHomePage= CStringHelper::ToUpper(self::HOMEPAGE_NAME);
            if( $tmpName != $tmpHomePage && $tmpName != 'I18N' && 
                !$this->m_pluginMap->Has($pluginName) && 
                $this->m_enableList->Has($pluginName) )
            {
                $plugin     = $this->LoadPlugin($pluginName);
                if( $plugin ){
                    $result->Add($pluginName, $plugin);  
                }
            }
        }
        CLog::Get()->Note('PluginManager loaded '. $result->Size().' plugins.');
        
        return $result;
    }
    
    public function LoadPlugin($pluginName)
    {    
        $pluginName = ucfirst($pluginName);
        if( $this->m_pluginMap->HasKey($pluginName) )
            return $this->m_pluginMap->Get($pluginName);
            
        $result = $this->PluginExists($pluginName);
        if( !$result )return null;
        $isInternalPlugin   = false;
        if( $result === 1 ){
            $isInternalPlugin   = true;
        }
        $class  = CPlugin::GetPluginClassName($pluginName);
        $plugin = new $class($pluginName, $isInternalPlugin);
        $plugin->Initialize();
        $this->m_pluginMap->Add($pluginName, $plugin);
        
        CLog::Get()->Note('PluginManager start loading the '.$pluginName.' plugin.');
        
        return $plugin;
    }
    
    public function PluginExists($pluginName, $throwExceptions = true)
    {
        $pluginName = ucfirst($pluginName);
        if( $pluginName != self::HOMEPAGE_NAME && !$this->m_enableList->Has($pluginName) )
        {
            $message    = sprintf('The plugin "%s" is not enabled.', $pluginName);
            if( $throwExceptions )
                throw new CException($message);
            CLog::Get()->Warning($message);
            return false;
        }
        
        // check internal plugins
        $internalPluginsPath= $this->GetPluginPath($pluginName, true);
        if( CFileHelper::IsExsit($internalPluginsPath) )
        {
            $result = $this->_PluginExists($internalPluginsPath, 
                $pluginName, $throwExceptions);
            if( $result )return 1;
        }
        
        // check custom plugins
        return $this->_PluginExists($this->GetPluginPath($pluginName), 
            $pluginName, $throwExceptions);
    }
    
    protected function _PluginExists($pluginPath, $pluginName, $throwExceptions = false)
    {
        $requisites = array(CPlugin::GetXmlFile(), CPlugin::GetPluginFile(), 
            CPlugin::GetActionFile());
        foreach ( $requisites as $file )
        {
            if( !CFileHelper::IsExsit($pluginPath.$file) )
            {
                $message    = sprintf('Can not found the file of '.
                    $pluginName.' plugin: '.$filename);
                if( $throwExceptions )
                    throw new CException($message);
                CLog::Get()->Warning($message);
                return false;
            }
        }
        
        $xmlFile    = $pluginPath.M_DS.CPlugin::GetXmlFile();
        $pluginFile = $pluginPath.M_DS.CPlugin::GetPluginFile();
        $actionFile = $pluginPath.M_DS.CPlugin::GetActionFile();
        
        $doc            = new DOMDocument();
        $doc->load($xmlFile);
        $nickNode       = $doc->getElementsByTagName('nick');
        $autherNode     = $doc->getElementsByTagName('auther');
        $versionNode    = $doc->getElementsByTagName('version');
        $descriptionNode= $doc->getElementsByTagName('description');
        if( !$nickNode->length || !$autherNode->length || 
            !$versionNode->length || !$descriptionNode->length)
        {
            CLog::Get()->Warning('This directory plugin XML file strcution error:<br>'.$pluginXml);
            return false;
        }
        
        require($pluginFile);
        require($actionFile);
        
        $pluginClassName    = CPlugin::GetPluginClassName($pluginName);
        $actionClassName    = CPlugin::GetActionClassName($pluginName);
        
        $checkClassList     = array($pluginClassName, $actionClassName);
        foreach ( $checkClassList as $class )
        {
            if (!class_exists($class, false))
            {
                $message    = sprintf(
                    'There is no "%s" class in your plugin file "%s"', 
                     $class, $pluginFile);
                if( $throwExceptions )
                    throw new CException($message);
                CLog::Get()->Warning($message);
                return false;
            }
        }
        
        return true;
    }
    
    public function _LoadPluginConfigData($isGenerate, $origin, $cache, $standby)
    {
        if( count($standby) != 1 ){
            throw new CExceptionInvalidArgument(
                'Missing parameter, the last paramter must be plugin instance.');
        }
        $plugin = $standby[0];
        $plugin->_LoadXmlData($isGenerate, $origin, $cache);
    }
    
    public function & GetPlugins()
    {
        return $this->m_pluginMap;
    }
    
    public function & GetPlugin($pluginName)
    {
        return $this->m_pluginMap->Get($pluginName);
    }
    
    public function Has($pluginName)
    {
        return $this->m_pluginMap->Has($pluginName);
    }
    
    public function Size()
    {
        return $this->m_pluginMap->Size();
    }
    
    public function GetPluginsPath($isInternal = false)
    {
        if( $isInternal )
        {
            return $this->m_internalPath;
        }
        return $this->m_pluginsPath;
    }
    
    static public function GetPluginPath($pluginName, $isInternal = false)
    {
        $pluginsPath    = self::Get()->GetPluginsPath($isInternal);
        $pluginsPath    = CFileHelper::CatDS($pluginsPath);
        return $pluginsPath.$pluginName.M_DS;
    }
}
?>
